package fr.cephb.operon.server.simple;

import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;

import javax.xml.XMLConstants;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

import fr.cephb.joperon.core.Operon;
import fr.cephb.joperon.core.bio.Assembly;
import fr.cephb.joperon.core.bio.Chromosome;
import fr.cephb.joperon.core.db.SnpMapDB;
import fr.cephb.joperon.core.entities.PrjSeq;
import fr.cephb.joperon.core.entities.SnpMap;
import fr.cephb.joperon.core.entities.SnpSeq;
import fr.cephb.joperon.tool.OperonFactory;
import fr.cephb.operon.common.HttpApplication;


public class SNPHandler
	extends HttpApplication
	{
	private static final String RDF_NS= "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
	private static final String NS= "http://ontology.cng.fr/snp#";
	private static final String RDFS_NS= "http://www.w3.org/2000/01/rdf-schema#";
	
	private static final String PREFIX= "op";
	
	private static final String DEFAULT_CONTEXT="/snps";
	private Assembly assembly= Assembly.getDefaultAssembly();
	
	private SNPHandler()
		{
		
		
		}
	
	
	public Assembly getAssembly() {
		return assembly;
	}
	
	@Override
	protected void usage(PrintStream out) {
		out.println("Author: Pierre Lindenbaum PhD.");
		out.println("A Simple Monothreaded HTTP server returning RDF information about a position on the genome");
		super.usage(out);
		out.println(" -c <web-context> default:"+DEFAULT_CONTEXT);
		out.println(" -a <assemby-id> default:"+Assembly.getDefaultAssembly().getId());
		for(Assembly as: Assembly.getAssemblies())
			{
			out.println("   "+as.getId()+" : "+as.getName());
			}
		}
	@Override
	public void handle(HttpExchange exchange) throws IOException
		{
		Chromosome chrom=null;
		Integer start=null;
		Integer end=null;
		try
			{
	        for(Parameter p: getParameters(exchange))
	        	{
	        	if(p.getName().equalsIgnoreCase("chrom"))
	        		{
	        		chrom= Chromosome.getByName(p.getValue());
	        		if(chrom==Chromosome.ChrUndefined) chrom=null;
	        		}
	        	else if(p.getName().equalsIgnoreCase("start"))
		    		{
		    		start=Integer.parseInt(p.getValue());
		    		}
	        	else if(p.getName().equalsIgnoreCase("end"))
		    		{
		    		end=Integer.parseInt(p.getValue());
		    		}
	        	}
			}
		catch(Throwable err)//case of bad cast e.g. start=HELLO
			{
			throw new IOException(err);
			}
       
        
        Headers responseHeaders = exchange.getResponseHeaders();
        responseHeaders.set("Content-Type", "application/rdf+xml");
      //* response is OK (200)
        exchange.sendResponseHeaders(200, 0);
		
      
        // Get response body
        Operon operon=null;
        OperonFactory factory=null;
        PrintWriter rout = new PrintWriter(exchange.getResponseBody());
        XMLOutputFactory xmlfactory= XMLOutputFactory.newInstance();
        XMLStreamWriter w=null;
        SnpMapDB.ValueIterator iter=null;
        try
        	{
        	operon= Operon.newInstance(super.getBerkeleyDBHome());
        	w=xmlfactory.createXMLStreamWriter(rout);
        	w.writeStartDocument();
        	
        	w.writeStartElement("rdf", "RDF", RDF_NS);
        	w.writeAttribute("xmlns", XMLConstants.XML_NS_URI, "rdf", RDF_NS);
        	w.writeAttribute("xmlns", XMLConstants.XML_NS_URI, PREFIX, NS);
        	w.writeAttribute("xmlns", XMLConstants.XML_NS_URI, "rdfs", RDFS_NS);
        	
        	
        	w.writeComment("We use rdf ID based on LSID, but is not supported (fake lsid)");
        	if(chrom!=null && start!=null && end!=null && start< end)
        		{
        		log().info("dump snp on "+chrom+" between "+start+"-"+end);
        		factory= new OperonFactory(operon);
        		iter=factory.getSnpMap(getAssembly()).listBetween(chrom, start, end);
        		while(iter.hasNext())
        			{
        			SnpMap snp= iter.next();
        			if(!snp.getAcn().matches("rs[0-9]+")) continue;
        			
        			w.writeStartElement(PREFIX, "SnpMap", NS);
        			w.writeAttribute(RDF_NS, "about", "urn:lsid:cng.fr:SnpMap:"+snp.getFid().asString());
        			printElement(w,"name",snp.getAcn());
        			w.writeEmptyElement("rdfs", "seeAlso", RDFS_NS);
        			w.writeAttribute(RDF_NS, "resource", "http://www.ncbi.nlm.nih.gov/SNP/snp_ref.cgi?rs="+snp.getAcn().substring(2));
        			
        			
        			/** write position */
        			w.writeStartElement(PREFIX, "hasPosition", NS);
        			w.writeStartElement(PREFIX, "Fid", NS);
        			
        			w.writeEmptyElement(PREFIX, "assembly",NS);
        			
        			
        			w.writeEmptyElement(PREFIX, "chromosome", NS);
        			w.writeAttribute(RDF_NS, "resource", "urn:lsid:cng.fr:Chromosome:"+getAssembly().getShortCode()+"_"+snp.getFid().getChromosome().toString());
        			printElement(w,"position",String.valueOf(snp.getFid().getPosition()));
        			printElement(w, "fid", snp.getFid().asString());
        			w.writeEndElement();
        			w.writeEndElement();
        			
        			/** write fasta */
        			SnpSeq snpseq= factory.getSnpSeq(getAssembly()).get(snp.getFid());
        			if(snpseq!=null)
        				{
        				w.writeStartElement(PREFIX, "hasFasta", NS);
            			w.writeCharacters( snpseq.asFastaSequence() );
            			w.writeEndElement();
        				}
        			/** write designs */
        			PrjSeq prjseq= factory.getPrjSeq(getAssembly()).get(snp.getFid());
        			if(prjseq==null)
        				{
        				w.writeComment("no design found for this SNP");
        				}
        			else
        				{
        				printElement(w,"hasTaqmanDesign",prjseq.asTaqmanRow());
        				printElement(w,"hasIlluminaDesign",prjseq.asIlluminaRow(getAssembly()));
        				printElement(w,"hasSnplexDesign",prjseq.asSnplexRow());
        				printElement(w,"hasSequenomDesign",prjseq.asSequenomRow());
        				}
        			
        			w.writeEndElement();
        			w.writeCharacters("\n");
        			}
        		}
        	w.writeEndElement();
        	w.writeEndDocument();
        	}
  
        catch (Exception e) {
        	e.printStackTrace();
			throw new IOException(e);
        	}
        finally
        	{
        	if(iter!=null) iter.close();
        	if(factory!=null) factory.close();
        	if(operon!=null) operon.safeClose();
        	if(w!=null )
        		{
        		try{
        			w.flush();
        			w.close();
        			}
        		catch (XMLStreamException e)
        			{
        			}
        		}
        	}
        rout.flush();
        rout.close();
		}
	
	private static void printElement(XMLStreamWriter w,String local,String text)
		throws XMLStreamException
		{
		w.writeStartElement(PREFIX, local, NS);
		w.writeCharacters(text);
		w.writeEndElement();
		}
	
	@Override
	protected int parseOption(String[] args, int optind)
		{
		int i= super.parseOption(args, optind);
		if(i!=-1) return i;
		//assembly
		if(args[optind].equals("-a"))
			{
			this.assembly= Assembly.getAssemblyById(Integer.parseInt(args[++optind]));
			}
		else
			{
			return -1;
			}
		return optind;
		}
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			SNPHandler app= new SNPHandler();
			String context=null;
			int optind=0;
			int index=0;
			//loop over arguments
		    while(optind<args.length)
				{
				if((index=app.parseOption(args,optind))!=-1)
				 	{
					optind= index; 
				 	}
				else if (args[optind].equals("-c"))
				     {
				     context=args[++optind];
				     }
				 else if (args[optind].equals("--"))
				     {
				     ++optind;
				     break;
				     }
				else if (args[optind].startsWith("-"))
				     {
				     System.err.println("bad argument " + args[optind]);
				     System.exit(-1);
				     }
				else
				     {
				     break;
				     }
				++optind;
				}
	
		    
		    if(context==null)
		    	{
		    	context=DEFAULT_CONTEXT;
		    	}
		  
		    
		    
		    //check assembly
		    if(app.assembly==null)
		    	{
		    	app.assembly= Assembly.getDefaultAssembly();
		    	app.log().info("Set assembly to "+app.assembly);
		    	}
		    app.log().info("Starting Server context "+context+" on port "+app.getPort());
		    app.log().info("Press Ctrl C to end");
		    HttpServer server = HttpServer.create(new InetSocketAddress(app.getPort()), 0);
            server.createContext(context, new SNPHandler());
            server.start();

			}
		catch (Exception e)
			{
			e.printStackTrace();
			}

	}

}
